#include "GLWidget.h"
#include <stdlib.h>
#define _USE_MATH_DEFINES 1
#include <cmath>
#include <ctime>
#include <sstream>

#define PI		3.14159265358979f 


GLWidget::GLWidget(QWidget * parent):QGLWidget(parent)
{
	setFocusPolicy(Qt::ClickFocus);

	connect( &timer, SIGNAL(timeout()), this, SLOT(updateGL()) );
	timer.start(50);
	connect( &timerfps, SIGNAL(timeout()), this, SLOT(timerFPSslot()));
	timerfps.start(1000);

	resetedFPS = true;
	frames = 0;

	m_vel = 1.0;
	m_dx  = 1.0;
	m_dt  = 1.0;
	m_t   = 0.0;

	m_intervals = 100;
	m_repeticions = 1;

	m_apartat = APARTAT_A;
	setValorsInicials();

	m_drawBoth = true;
	m_drawLeft = false;
	m_drawRight = false;
	m_running = true;
	m_dobleOna = false;
	m_drawZero = false;

	mInitialized = false;
}


void GLWidget::initializeGL()
{
	if (mInitialized) return;
	
	glClearColor(0.35f, 0.35f, 0.6f, 1.0f);
	glEnable(GL_DEPTH_TEST);

	time0 = timef = clock();

	emit enableWaveSelectors(m_dobleOna);

	mInitialized = true;

}



void GLWidget::paintGL( void )
{ 
	if (!mInitialized) return;


	if (m_running) computeOna();


	// Esborrem el buffer de color i de profunditat
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//gluPerspective(60.0f, m_ratio, 0.01, 1000);	
	glOrtho(-1, m_repeticions * m_intervals + 1, 
			-3, 3, 
			 0.01, 1000);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0, 0, -100);

	if (m_drawZero) {
		glColor3f(1, 1, 1);
		glBegin(GL_LINE_STRIP);
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(m_intervals * m_repeticions, 0.0f, 0.0f);
		glEnd();
	}

	glLineWidth(2.0f);

	if (m_drawLeft || !m_dobleOna) {
		if (m_dobleOna) glColor3f(0, 0, 1);
		else			glColor3f(1, 0, 0);
		glBegin(GL_LINE_STRIP);
		for (int i = 0; i < m_intervals * m_repeticions; i++) {
			int idv = i % m_intervals;
			glVertex3f(i, m_ona[idv], 0.0f);
		}
		glEnd();
	}

	if (m_dobleOna) {
		if (m_drawRight) {
			glColor3f(0, 1, 0);
			glBegin(GL_LINE_STRIP);
			for (int i = 0; i < m_intervals * m_repeticions; i++) {
				int idv = i % m_intervals;
				glVertex3f(i, m_ona2[idv], 0.0f);
			}
			glEnd();
		}

		if (m_drawBoth) {
			glLineWidth(3);
			glColor3f(1, 0, 0);
			glBegin(GL_LINE_STRIP);
			for (int i = 0; i < m_intervals * m_repeticions; i++) {
				int idv = i % m_intervals;
				glVertex3f(i, m_ona[idv] + m_ona2[idv], 0.0f);
			}
			glEnd();
		}
	}

	glLineWidth(1.0f);
	

	frames++;
}




void GLWidget::computeOna() {

	std::vector<double> res (m_intervals);
	std::vector<double> res2(m_intervals);

	for (int i = 0; i < m_intervals; i++) {

		// restar la previa (x, t - Dt)
		res[i]  = - m_onaPrev[i];
		res2[i] = - m_onaPrev2[i];

		// sumar l'esquerra de l'actual
		double leftval, leftval2;
		if (i - 1 >= 0) {
			leftval  = m_ona [i - 1];
			leftval2 = m_ona2[i - 1];
		}
		else {
			switch (m_apartat) {
				case APARTAT_B:	leftval = 0.0;		 break;
				case APARTAT_C: leftval  = m_ona[0]; break;
				case APARTAT_G: leftval = 0.1 * sin(4*PI/m_intervals*m_t); break;
				default:		leftval  = m_ona [m_intervals - 1]; 
								leftval2 = m_ona2[m_intervals - 1];
								break;
			}
		}
		res[i]  += leftval;
		res2[i] += leftval2;

		// sumar la dreta de l'actual
		double rightval, rightval2;
		if (i + 1 < m_intervals) {
			rightval  = m_ona [i + 1];
			rightval2 = m_ona2[i + 1];
		}
		else {
			switch (m_apartat) {
				case APARTAT_B:	rightval = 0.0;				 	 break;
				case APARTAT_C: rightval = m_ona[m_intervals - 1]; break;
				case APARTAT_G: rightval = 0.0;  break;
				default:		rightval  = m_ona [0]; 
								rightval2 = m_ona2[0];
								break;
			}
		}
		res[i]  += rightval;
		res2[i] += rightval2;

		/*res[i] =   m_ona[(i - 1 + m_intervals)%m_intervals]
		         + m_ona[(i + 1)%m_intervals] 
		         - m_onaPrev[i];*/
	}

	m_onaPrev = m_ona;
	m_ona	  = res;

	m_onaPrev2 = m_ona2;
	m_ona2	   = res2;

	m_t += m_dt;
}



void GLWidget::setValorsInicials() {

	m_ona.resize(m_intervals);
	m_onaPrev.resize(m_intervals);
	m_ona2.resize(m_intervals);
	m_onaPrev2.resize(m_intervals);

	switch(m_apartat) {
		case APARTAT_A: setValorsA(); break;
		case APARTAT_B: setValorsB(); break;
		case APARTAT_C: setValorsC(); break;
		case APARTAT_D: setValorsD(); break;
		case APARTAT_E: setValorsE(); break;
		case APARTAT_F: setValorsF(); break;
		case APARTAT_G: setValorsG(); break;
		default: break;
	}

	m_t = 0.0;
}




void GLWidget::setValorsA() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]     = exp(-(i*m_dx - 10)*(i*m_dx - 10));
		m_onaPrev[i] = exp(-(i*m_dx - 10 + m_vel*m_dt)*(i*m_dx - 10 + m_vel*m_dt));
		m_ona2[i] = m_onaPrev2[i] = 0;
	}
}

void GLWidget::setValorsB() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]     = exp(-(i*m_dx - 10)*(i*m_dx - 10));
		m_onaPrev[i] = exp(-(i*m_dx - 10 + m_vel*m_dt)*(i*m_dx - 10 + m_vel*m_dt));
		m_ona2[i] = m_onaPrev2[i] = 0;
	}
}

void GLWidget::setValorsC() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]     = exp(-(i*m_dx - 10)*(i*m_dx - 10));
		m_onaPrev[i] = exp(-(i*m_dx - 10 + m_vel*m_dt)*(i*m_dx - 10 + m_vel*m_dt));
		m_ona2[i] = m_onaPrev2[i] = 0;
	}
}

void GLWidget::setValorsD() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]      = exp(-(i*m_dx - 10)*(i*m_dx - 10));
		m_onaPrev[i]  = exp(-(i*m_dx - 10 + m_vel*m_dt)*(i*m_dx - 10 + m_vel*m_dt));
		m_ona2[i]     = exp(-(i*m_dx - 90)*(i*m_dx - 90));
		m_onaPrev2[i] = exp(-(i*m_dx - 90 - m_vel*m_dt)*(i*m_dx - 90 - m_vel*m_dt));
	}
}

void GLWidget::setValorsE() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]      = sin(4*PI*double(i)/m_intervals);
		m_onaPrev[i]  = sin(4*PI*double(i + m_vel * m_dt)/m_intervals); 
		m_ona2[i]     = sin(4*PI*double(i)/m_intervals);
		m_onaPrev2[i] = sin(4*PI*double(i - m_vel * m_dt)/m_intervals);
	}
}

void GLWidget::setValorsF() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]      = sin(10*PI*double(i)/m_intervals);
		m_onaPrev[i]  = sin(10*PI*double(i + m_vel * m_dt)/m_intervals);
		m_ona2[i]     = sin(12*PI*double(i)/m_intervals);
		m_onaPrev2[i] = sin(12*PI*double(i + m_vel * m_dt)/m_intervals);
	}
}

void GLWidget::setValorsG() {
	for (int i = 0; i < m_intervals; i++) {
		m_ona[i]  = m_onaPrev[i] = 0;
		m_ona2[i] = m_onaPrev2[i] = 0;
	}
}



// EVENTS

void GLWidget::resizeGL (int width, int height)
{
	glViewport(0, 0, width, height);
	m_ratio = float(width)/float(height);
}



void GLWidget::keyPressEvent(QKeyEvent *e)
{
    e->ignore();
}

void GLWidget::keyReleaseEvent(QKeyEvent *e)
{
    e->ignore();
}

void GLWidget::mousePressEvent(QMouseEvent *e){
	e->ignore();
}

void GLWidget::mouseReleaseEvent(QMouseEvent *e)
{
	e->ignore();
}

void GLWidget::mouseMoveEvent(QMouseEvent *e)
{ 
	e->ignore();
}










/**********************************************/
/*					SLOTS					  */
/**********************************************/


//
// FPS
//

void GLWidget::timerFPSslot() {
	updateFPS(0.0); //arreglar el conteig de fps
}

void GLWidget::updateFPS(float fps) {
	fps = frames;
	if (resetedFPS) {
		minFPS = fps;
		maxFPS = fps;
		avgFPS = fps;
		avgFPSc = 0;
		resetedFPS = false;
	}
	else {
		if (fps < minFPS) minFPS = fps;
		if (fps > maxFPS) maxFPS = fps;
		avgFPS = (avgFPS*avgFPSc + fps*5) / float(avgFPSc + 5);
		//avgFPS = fps;
	}
	avgFPSc++;
	frames = 0;
}

